/**
 * authMiddleware.js - 身份验证中间件
 * 
 * 这个模块提供了身份验证和授权中间件，用于保护API端点。
 * 它验证JWT令牌并检查用户权限。
 */

const jwt = require('jsonwebtoken');
const { ApiError } = require('../utils/errors');
const config = require('../config');
const userService = require('../services/userService');

/**
 * 身份验证中间件
 * 
 * 验证请求中的JWT令牌，并将用户信息添加到请求对象
 * @param {Object} req - Express请求对象
 * @param {Object} res - Express响应对象
 * @param {Function} next - Express下一个中间件函数
 */
const authenticate = async (req, res, next) => {
  try {
    // 从请求头中获取令牌
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new ApiError(401, '未提供身份验证令牌');
    }

    // 提取令牌
    const token = authHeader.split(' ')[1];

    // 验证令牌
    let decoded;
    try {
      decoded = jwt.verify(token, config.jwt.secret);
    } catch (error) {
      if (error.name === 'TokenExpiredError') {
        throw new ApiError(401, '令牌已过期');
      }
      throw new ApiError(401, '无效的令牌');
    }

    // 获取用户信息
    const user = await userService.getUserById(decoded.id);

    // 检查用户状态
    if (user.status !== 'active') {
      throw new ApiError(403, '账户已被禁用');
    }

    // 将用户信息添加到请求对象
    req.user = {
      id: user.id,
      username: user.username,
      email: user.email,
      role: user.role,
    };

    next();
  } catch (error) {
    next(error);
  }
};

/**
 * 授权中间件
 * 
 * 检查用户是否具有所需的角色
 * @param {string|string[]} roles - 所需的角色或角色数组
 * @returns {Function} - Express中间件函数
 */
const authorize = (roles) => {
  return (req, res, next) => {
    try {
      // 确保用户已通过身份验证
      if (!req.user) {
        throw new ApiError(401, '未经身份验证');
      }

      // 如果没有指定角色，则允许所有已验证的用户
      if (!roles) {
        return next();
      }

      // 将单个角色转换为数组
      const requiredRoles = Array.isArray(roles) ? roles : [roles];

      // 检查用户是否具有所需的角色
      if (!requiredRoles.includes(req.user.role)) {
        throw new ApiError(403, '没有足够的权限');
      }

      next();
    } catch (error) {
      next(error);
    }
  };
};

module.exports = {
  authenticate,
  authorize,
};